package moldat.utilities;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import javax.xml.stream.XMLStreamException;
import moldat.ApplicationConfig;
import moldat.GeneExpression;
import moldat.Protein;
import moldat.SQLiteDB;
import uk.ac.ebi.kraken.interfaces.uniprot.DatabaseType;
import uk.ac.ebi.kraken.interfaces.uniprot.Gene;
import uk.ac.ebi.kraken.interfaces.uniprot.ProteinDescription;
import uk.ac.ebi.kraken.interfaces.uniprot.UniProtEntry;
import uk.ac.ebi.kraken.interfaces.uniprot.comments.AlternativeProductsComment;
import uk.ac.ebi.kraken.interfaces.uniprot.comments.AlternativeProductsIsoform;
import uk.ac.ebi.kraken.interfaces.uniprot.comments.CommentType;
import uk.ac.ebi.kraken.interfaces.uniprot.dbx.ensembl.Ensembl;
import uk.ac.ebi.kraken.uuw.services.remoting.EntryRetrievalService;
import uk.ac.ebi.kraken.uuw.services.remoting.UniProtJAPI;

/**
 * Diese Klasse laedt alle Informationen von UniProt herunter und erzeugt daraus
 * Protein-Objekt. Dazu wird die Bibliotherk 'UniProtJAPI' benutzt. Die so
 * erzeugten 'Proteine' werden dann in der Datenbank gespeichert.
 *
 * @author Marc Zoeller
 */
public class Importer {

    private static Importer importer = null;
    EntryRetrievalService retrieval = null;

    /**
     * Liefert eine Instanz der Importer-Klasse. Diese Methode sollte anstelle
     * des Default-Konstruktors benutzt werden.
     *
     * @return eine Instanz dieser Klasse
     */
    public static Importer getInstance() {
        if (importer == null)
            importer = new Importer();
        return importer;
    }

    /**
     * Diese Methode imporiert ein Protein zu einer UniProtID in die Datenbank.
     * Es wird eine Verbindung zu UniProt hergestellt und alle wichtigen
     * Informationen heruntergeladen und zu einem 'Protein' zusammengefasst.
     * Dieses wird dann in der Datenbank gespeichert.
     *
     * @see moldat.Protein
     * @param uniProtID UniProtID die importiert werden soll
     * @throws FileNotFoundException
     * @throws IOException
     * @throws SQLException
     */
    public void importID(String uniProtID) throws FileNotFoundException, IOException, SQLException, XMLStreamException {
        //UniProt Zugriff
        if (retrieval == null)
            retrieval = UniProtJAPI.factory.getEntryRetrievalService();
        if (SQLiteDB.getProtein(uniProtID) != null) {
            System.out.println("Skipped ID " + uniProtID);
            return;
        }

        //Abfragen der UniProtID
        UniProtEntry entry = (UniProtEntry) retrieval.getUniProtEntry(uniProtID);
        if (entry != null) {
            ApplicationConfig.setImportDetails("got uniprotid " + uniProtID);
            ProteinDescription description = entry.getProteinDescription();
            if (description.getRecommendedName().getFields().isEmpty())
                return;
            String name = description.getRecommendedName().getFields().get(0).getValue();

            String geneName = null;
            List<String> geneSynonyms = new LinkedList<String>();
            for (Gene gene : entry.getGenes()) {
                if (gene.hasGeneName()) {
                    geneName = gene.getGeneName().getValue();
                    geneSynonyms = GeneNameParser.getSynonyms(geneName);
                }
            }
            ApplicationConfig.setImportDetails("Found  " + geneSynonyms.size() + " Synonyms");

            List<String> isoforms = new ArrayList<String>();
            List<AlternativeProductsComment> comments = entry.getComments(CommentType.ALTERNATIVE_PRODUCTS);
            for (AlternativeProductsComment com : comments) {
                for (AlternativeProductsIsoform isoform : com.getIsoforms()) {
                    try {
                        int i = Integer.parseInt(isoform.getName().getValue());
                        isoforms.add("Isoform - " + isoform.getName().getValue());
                    } catch (NumberFormatException ex) {
                        isoforms.add(isoform.getName().getValue());
                    }
                }

            }
            ApplicationConfig.setImportDetails("Found " + isoforms.size() + " isforms");

            // ArrayExpress Zugriff
            List<Ensembl> ensemble = entry.getDatabaseCrossReferences(DatabaseType.ENSEMBL);
            String arrayExpressUrl = "";
            if (!ensemble.isEmpty())
                arrayExpressUrl = ensemble.get(0).getEnsemblGeneIdentifier().toString();    //nur noch die id des genes eintragen!
            ApplicationConfig.setImportDetails("Loading GeneExpression for: " + geneName);
            GeneExpression expression = null;
            if (geneName != null)
                expression = new GeneExpression(arrayExpressUrl);
            ApplicationConfig.setImportDetails("done Loading");

            //Fehlerbehandlung
            if (geneName == null)
                geneName = "unknown";
            if (name == null)
                name = "unknown";

            //Erstellen des Proteins
            Protein p;
            if (geneSynonyms.isEmpty())
                p = new Protein(name, geneName, uniProtID, arrayExpressUrl, isoforms, expression);
            else
                p = new Protein(name, geneName, uniProtID, arrayExpressUrl, isoforms, expression, geneSynonyms);
            ApplicationConfig.setImportDetails("Adding Protein " + name + " to Database");
            SQLiteDB.addProtein(p);
            ApplicationConfig.setImportDetails("Protein " + name + " imported");
        }
    }

    /**
     * Diese Methode importiert eine Collection mit UniProtIDs.
     *
     * @see #importID(java.lang.String)
     * @param uniProtIDs UniProtIDs die importiert werden sollen
     * @throws FileNotFoundException
     * @throws IOException
     * @throws SQLException
     */
    public void importAll(Collection<String> uniProtIDs) throws FileNotFoundException, IOException, SQLException, XMLStreamException {
        int idCount = uniProtIDs.size();
        ApplicationConfig.setImportProgress("0 / " + idCount);
        int currentid = 0;
        for (String id : uniProtIDs) {
            importID(id);
            ApplicationConfig.setImportProgress(++currentid + " / " + idCount);
        }
    }
}
